#include <netinet/ip.h>
#include "scps.h"
#include "scps_ip.h"
#include "scpsudp.h"
#include "tp_debug.h" 
#include "tap.h"
#include "other_proto_handler.h"
#include "rs_config.h"

extern struct msghdr out_msg;
extern struct llbuf *in_data;


udp_Header *up;

extern GW_ifs gw_ifs;

/*
 * Pass in a requirements structure and generate a 
 * template for it; Pass the template back to the 
 * caller.
 */

int ip_get_template (scps_np_rqts * rqts, ip_template * ip_templ)
{
  ip_templ->nl_head.ipv4.vht = htons (0x4500 | rqts->DSCP);
  ip_templ->nl_head.ipv4.identification = htons (rqts->tpid);
  ip_templ->nl_head.ipv4.frag = 0;
/* 
 * The following is actually wrong; 
 * Use of the Security Protocol should be determined on a socket-by-socket 
 * basis, not mandated at compile time. This needs to be
 * fixed.
 */
  ip_templ->nl_head.ipv4.ttlProtocol = htons ((250 << 8) + rqts->tpid);
  ip_templ->nl_head.ipv4.checksum = 0;
  ip_templ->nl_head.ipv4.source = htonl (rqts->ipv4_src_addr);
  ip_templ->nl_head.ipv4.destination = htonl (rqts->ipv4_dst_addr);
  ip_templ->nl_head.ipv4.length = 0;
  ip_templ->protocol_fam = NL_PROTOCOL_IPV4;
  return (20);

}

/* This function looks like ip_request, but all I want it
 * to do is tell me how int32_t the ip-header will be so I 
 * can make room for it in the mbuffer;
 */
int ip_header_len (void)
{
  /* This is good enough for right now */
  return (20);
}

/* 
 * The "one-shot" approach to sending an ip datagram; 
 * The requirements structure is passed down, and a template
 * is generated for it prior to calling ip_trequest. This is
 * only going to be used for connectionless transport protocols
 * (unless they've called connect() to bind to a destination address
 * 
 * An obvious improvement to this code would be to cache the last
 * generated template since odds are that it will be identical to
 * the next one generated (packet-trains)
 * 
 */

int ip_request (tp_Socket * s, struct mbuff *m, uint32_t * bytes_sent)
{
  ip_template ph;
  int cc;

  ip_get_template (&(s->np_rqts), &ph);
  cc = ip_trequest (s, m, bytes_sent);
  return (cc);
}

/* 
 * Coallesce a locally generated network protocol header and the
 * data passed down to me, then push it onto the appropriate network
 * interface.
 *
 * This function (and all it's equivalents (PATH, SCPS-NP, etc.) 
 * need the following passed in as parameters:
 *
 * int
 * ip_trequest (tp_Socket *s, struct mbuff *m, uint32_t *bytes_sent) 
 * tp_Socket    *s           <- The owning socket
 * struct mbuff *m           <- The transport & SP header and actual data
 * uint32_t     *bytes_sent  <- The number of bytes we expect to send
 *
 * I want this to pass me a iov_vector instead of the data and then
 * I want ot push out the iov_vector with a writev()
 */

int ip_trequest (tp_Socket * s, struct mbuff *m, uint32_t * bytes_sent)
{
  uint32_t length;
  struct gwio *gwio;
  struct addrs next_hop;
  int cc;


      memcpy (&(s->np_rqts.dst_mac_addr [0]), &(s->dst_mac_addr [0]), 6);
      memcpy (&(s->np_rqts.src_mac_addr [0]), &(s->src_mac_addr [0]), 6);
      s->np_rqts.frame_type = s->frame_type;
      s->np_rqts.recv_tap_if = s->recv_tap_if;

  /* Setup the out_msg structure and leave space for:
   *   slot[0]  : raw link protocol packet (or Raw-IP)
   *   slot[1]  : network protocol header (to be built here)
   *   slot[2]  : the security and transport protocol headers
   */

  out_msg.msg_iovlen = buff_vec (m, &out_msg, 3);

  /*
   * Attach the security and transport headers in the mbuff */

  out_msg.msg_iov[2].iov_base = m->m_pktdat + s->th_off;
  out_msg.msg_iov[2].iov_len = m->m_len;

  /* Was... */
  /* length = m->m_len + m->m_ext.len + s->np_size; */
  length = m->m_len + ((m->m_flags & M_EXT) ? 1 : 0) * m->m_ext.len + s->np_size;

  /* 
   * Initial length is the size of the data plus
   * transport & security headers already in mbuff
   * plus the header we are building here.
   */

  if (length > MAX_MTU)
    {
      SET_ERR (SCPS_EMSGSIZE);
      return (-1);
    }

  /* 
   * I've got the prototype IP header in s->ip_templ, so I should have
   * all the information that I need except the length of this header...
   */

  s->ip_templ.nl_head.ipv4.length = htons (length);  /* Could have done this asignment above */
  s->ip_templ.nl_head.ipv4.checksum = 0;
  s->ip_templ.nl_head.ipv4.identification++;
  s->ip_templ.nl_head.ipv4.checksum = ~checksum ((uint16_t *) & (s->ip_templ),

         inv4_GetHdrlenBytes ((in_Header *) & (s->ip_templ)));

  /* Stuff the data into the iovec */


  out_msg.msg_iov[1].iov_base = (void *) &(s->ip_templ.nl_head.ipv4);
  out_msg.msg_iov[1].iov_len = s->np_size;

  /* We really need to decide which interface to go out on... */
  /* We can move this into the np_rqts structure as the default interface */

  if (s->np_rqts.gwio)
    gwio = s->np_rqts.gwio;
  else
    gwio = scheduler.gwio;


  next_hop.nl_protocol = NL_PROTOCOL_IPV4;
  next_hop.nl_head.ipv4_addr = s->ip_templ.nl_head.ipv4.destination;

  cc = ll_iovsend (gwio, next_hop,
       s->np_rqts.tpid, (int) length, &out_msg, s->rt_route,&(s->np_rqts));

  cc += s->np_size;
  *bytes_sent = cc;
  return (cc);
}


int ip_ind (scps_np_rqts * rqts, int length, int *data_offset)
{
  int cc = 0, len = 0;
  int offset = 0;
  uint16_t ip_cks = 0;
  ip_template *ip;
  struct gwio *gwio;
  int total_offset = 0;
  unsigned ip_frag;

  int rc;
  unsigned char proto;

  /* Find an interface with data */

  for (gwio = ((struct gwio *) scheduler.gwio); 
    (gwio && TAILQ_EMPTY(&gwio->incoming));
    gwio = gwio->next);
  LOG("ip_ind start \n");
  if ((gwio && (cc = ll_nbreceive (gwio, &in_data, MAX_MTU, &offset)) > 0)) {
    LOG("ip ind address 0x%x\n", gwio->address);
    in_data->gwio = gwio;

    {
    rqts->gwio = gwio;
    rc = tap_ind (gwio, &in_data, MAX_MTU, &offset, rqts);

    if (rc == 0) 
      return (-1);

  ip = (in_Header *) ((void *) in_data->data + in_data->offset);

  if ((ntohs (ip->nl_head.ipv4.vht) & 0xf000) != 0x4000) {
    in_data ->offset -= total_offset;
    in_data->offset -= ETH_HLEN;
    in_data->size +=ETH_HLEN;
    TAILQ_INSERT_TAIL(&gwio->incoming, in_data, entry);
    scheduler.gwio_data++;
    if ((ntohs (ip->nl_head.ipv4.vht) & 0xf000) != 0x4000) {
      return (NL_TRY_NP);
    }
    return (NL_TRY_NP);
  }
  cc = ntohs (ip->nl_head.ipv4.length);

  ip = (in_Header *) ((void *) in_data->data + in_data->offset);
  proto = inv4_GetProtocol (ip);
  LOG("ip ind proto %u\n", proto);
  switch (proto) {
  case IPPROTO_TCP:
  case SCPSCTP:
  case IPPROTO_ICMP:
    break;
  default:
   other_proto_Handler (gwio, &in_data, MAX_MTU, &offset, rqts, proto);
   return (-1);
   break;
  }
  /* Make sure that the checksum passes for the IP header */

  if (ip->nl_head.ipv4.checksum == 0 || (ip_cks = checksum ((uint16_t *) ip, inv4_GetHdrlenBytes (ip)) == 0xFFFF)) {
    /* Fill out the requirements template for the incoming packet */
    rqts->tpid = inv4_GetProtocol (ip);
    rqts->ipv4_dst_addr = ntohl (ip->nl_head.ipv4.destination);
    rqts->ipv4_src_addr = ntohl (ip->nl_head.ipv4.source);
    rqts->cksum = 0;
    rqts->gwio = gwio;
    rqts->nl_protocol = NL_PROTOCOL_IPV4;
    rqts->DSCP = (unsigned char ) ((ntohs (ip->nl_head.ipv4.vht)) & 0x00ff);
  } else {
    LOG ("IP checksum failed src = %ud dst = %ud\n", rqts->ipv4_src_addr, rqts->ipv4_dst_addr);
    fflush (stdout);
    rqts->gwio = gwio;
    rqts->tpid = 0;
  }
  *data_offset = offset + inv4_GetHdrlenBytes (ip);


    len = (int) (ntohs (ip->nl_head.ipv4.length) - inv4_GetHdrlenBytes (ip));
  }

   ip_frag = ip->nl_head.ipv4.frag;
    if ((ip_frag & IP_DF) == IP_DF) {

      // ONLY DF flag is set - this is ok
      return (len);
    }

    if (ip_frag & (IP_MF | IP_OFFMASK))  {
      LOG ("Gateway: Got a fragment\n");
      free_llbuf (rqts->gwio, in_data);
      return (-1);
    }
    return (len);
  }
  SET_ERR (SCPS_EWOULDBLOCK);
  return (-1);
}
